﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media.Animation;
using SlimDX;
using System.Diagnostics.CodeAnalysis;

namespace System.Windows.Media.Media3D10
{
    /// <summary>
    /// Fournit une classe parente pour toutes les transformations tridimensionnelles, y compris les transformations de mise à l'échelle, de rotation et de translation. 
    /// </summary>
    [Localizability(LocalizationCategory.None, Readability = Readability.Unreadable)]
    public abstract class Transform3D10 : GeneralTransform3D10
    {
        #region Data
        private static readonly Transform3D10 _identity = new IdentityTransform3D10();
        #endregion

        #region Public Properties
        /// <summary>
        /// Obtient la transformation d'identité. 
        /// </summary>
        public static Transform3D10 Identity { get { return _identity; } }

        /// <summary>
        /// Obtient la transformation inverse de cet objet, si possible.
        /// </summary>
        public override GeneralTransform3D10 Inverse
        {
            get
            {
                Transform3D10 clone = this.CloneCurrentValue();
                clone.Value.Invert();
                return clone;
            }
        }

        /// <summary>
        /// Obtient une valeur qui spécifie si la matrice est affine ou non.
        /// </summary>
        public abstract bool IsAffine { get; }

        /// <summary>
        /// Représentation matricielle de la transformation
        /// </summary>
        [SuppressMessage("Microsoft.Naming", "CA1721")]
        public abstract SlimDX.Matrix Value { get; }
        #endregion

        #region Public Methods
        /// <summary>
        /// Crée un clone modifiable de ce <see cref="Transform3D10"/>, en créant des copies complètes des valeurs de cet objet.
        /// Lors de la copie de propriétés de dépendance, cette méthode copie les références de ressources et les liaisons de données (même si elles ne peuvent plus être résolues), mais pas les animations ni leurs valeurs actuelles.
        /// </summary>
        /// <returns>
        /// Clone modifiable de l'objet actuel.
        /// La valeur de la propriété <see cref="Freezable.IsFrozen"/> de l'objet cloné est false, même si la valeur de la propriété <see cref="Freezable.IsFrozen"/> de la source est true.
        /// </returns>
        public new Transform3D10 Clone()
        {
            return base.Clone() as Transform3D10;
        }

        /// <summary>
        /// Crée un clone modifiable de ce <see cref="Transform3D10"/>, en créant des copies complètes des valeurs actuelles de cet objet.
        /// Contrairement à leurs valeurs actuelles, les références de ressources, les liaisons de données et les animations ne sont pas copiées.
        /// </summary>
        /// <returns>
        /// Clone modifiable de l'objet actuel.
        /// La valeur de la propriété <see cref="Freezable.IsFrozen"/> de l'objet cloné est false, même si la valeur de la propriété <see cref="Freezable.IsFrozen"/> de la source est true.
        /// </returns>
        public new Transform3D10 CloneCurrentValue()
        { 
            return base.CloneCurrentValue() as Transform3D10;
        }
        
        /// <summary>
        /// Transforme le tableau d'objets <see cref="SlimDX.Vector3"/> spécifié. 
        /// </summary>
        /// <param name="vectors">Tableau d'objets <see cref="SlimDX.Vector3"/> à transformer.</param>
        public void Transform(Vector3[] vectors)
        {
            if (object.ReferenceEquals(vectors, null))
                throw new ArgumentNullException("vectors");
            for (int i = 0; i < vectors.Length; i++)
            {
                Vector3 transformed = SlimDX.Vector3.TransformCoordinate(vectors[i], this.Value);
                vectors[i].X = transformed.X;
                vectors[i].Y = transformed.Y;
                vectors[i].Z = transformed.Z;
            }
        }

        /// <summary>
        /// Transforme le <see cref="SlimDX.Vector4"/> spécifié. 
        /// </summary>
        /// <param name="vector"><see cref="SlimDX.Vector4"/> à transformer.</param>
        /// <returns><see cref="SlimDX.Vector4"/> transformé.</returns>
        public Vector4 Transform(Vector4 vector)
        {
            SlimDX.Vector4 result;
            if (!TryTransform(vector, out result))
                throw new InvalidOperationException("Transform cannot be opered");
            return result;
        }

        /// <summary>
        /// Transforme le tableau d'objets <see cref="SlimDX.Vector4"/> spécifié. 
        /// </summary>
        /// <param name="vectors">Tableau d'objets <see cref="SlimDX.Vector4"/> à transformer.</param>
        public void Transform(Vector4[] vectors)
        {
            if (object.ReferenceEquals(vectors, null))
                throw new ArgumentNullException("vectors");
            for (int i = 0; i < vectors.Length; i++)
            {
                Vector4 transformed = SlimDX.Vector4.Transform(vectors[i], this.Value);
                vectors[i].X = transformed.X;
                vectors[i].Y = transformed.Y;
                vectors[i].Z = transformed.Z;
                vectors[i].W = transformed.W;
            }
        }

        /// <summary>
        /// Essaie de transformer le <see cref="SlimDX.Vector3"/> spécifié et retourne une valeur qui indique si la transformation a réussi.
        /// </summary>
        /// <param name="inVector"><see cref="SlimDX.Vector3"/> à transformer.</param>
        /// <param name="result">Résultat de la transformation de <paramref name="inVector"/>.</param>
        /// <returns>true si <paramref name="inVector"/> a été transformé ; sinon, false.</returns>
        public override bool TryTransform(Vector3 inVector, out Vector3 result)
        {
            try
            {
                result = SlimDX.Vector3.Transform(inVector, this.Value).ToVector3();
                return true;
            }
            catch (ArgumentException)
            {
                result = Vector3.Zero;
                return false;
            }
        }

        /// <summary>
        /// Essaie de transformer le <see cref="SlimDX.Vector4"/> spécifié et retourne une valeur qui indique si la transformation a réussi.
        /// </summary>
        /// <param name="inVector"><see cref="SlimDX.Vector4"/> à transformer.</param>
        /// <param name="result">Résultat de la transformation de <paramref name="inVector"/>.</param>
        /// <returns>true si <paramref name="inVector"/> a été transformé ; sinon, false.</returns>
        public bool TryTransform(Vector4 inVector, out Vector4 result)
        {
            try
            {
                result = SlimDX.Vector4.Transform(inVector, this.Value);
                return true;
            }
            catch (ArgumentException)
            {
                result = Vector4.Zero;
                return false;
            }
        }
        #endregion
    }
}
